/*************************************************************************
 *
 * MenuTask
 * 
 * (c) Ales Povalac, alpov@alpov.net, http://www.alpov.net/
 * 
 *************************************************************************
 *
 * $Id: menu.c 83 2006-08-11 09:25:54Z alpov $
 * 
 *************************************************************************/         

#define _MENU_C
#include <stdio.h>
#include <stdlib.h>
#include <inttypes.h>
#include <string.h>
#include <avr/pgmspace.h>
#include "../avrx/avrx.h"
#include "../avrx/AvrXFifo.h"
#include "beep.h"
#include "button.h"
#include "menu.h"
#include "lcd.h"
#include "menu_lcd.h"
#include "menu_stfn.h"
#include "data.h"
#include "datetime.h"
#include "locate.h"
#include "serial.h"

AVRX_DECL_FIFO(MenuFifo, 16);
TimerControlBlock MenuDelayTimer;
static unsigned char message;

#include "menu_def.h"


/*************************************************************************
 * zobrazeni zpravy
 *************************************************************************/ 
void MenuMessage(unsigned char msg)
{
    message = msg;
    AvrXFlushFifo(MenuFifo);
    AvrXPutFifo(MenuFifo, MENU_MESSAGE);
}


/*************************************************************************
 * refresh menu
 * interni funkce volana MenuTaskem
 * pro externi refresh: AvrXPutFifo(MenuFifo, MENU_REFRESH); 
 *************************************************************************/ 
void MenuInternalRefresh(unsigned char state)
{
    static char str[MAX_DESC_LEN];
    unsigned char year, month, day, hour, min, sec;
    
    lcd_clrscr();
    lcd_command(LCD_DISP_ON); 
    switch (state) {
        case ST_MAIN: {
            div_t temp;
            decode_time(rwdata.now, &hour, &min, &sec);
            
#if (DISPLAY == 1602)
            temp = div(stdata.temperature[eedata.sensor_index], 100);
            temp.rem = abs(temp.rem) / 10;
            ReadEEPromBlock(str, desc.sensor_desc[eedata.sensor_index], sizeof(str));
            lcd_gotoxy(0, 0);
            printf_P(PSTR(MSG_MAIN_TEMP), str, temp.quot, temp.rem);
            
            lcd_gotoxy(0, 1);
            if (eedata.info_index == 0) {
                if (stdata.state.mode == MODE_EKV)
                    printf_P(PSTR(MSG_MAIN_EKV), stdata.state.param, stdata.state.wall, stdata.state.dev);
                else if (stdata.state.mode == MODE_THSTAT)
                    printf_P(PSTR(MSG_MAIN_THSTAT), stdata.state.param, stdata.state.dev);
                else
                    printf_P(PSTR(MSG_MAIN_OFF));
                
            } else {
                printf_P(PSTR(MSG_MAIN_RTC), hour, min);
            }
            
#elif (DISPLAY == 2004)
            temp = div(stdata.temperature[eedata.sensor_index], 100);
            temp.rem = abs(temp.rem) / 10;
            ReadEEPromBlock(str, desc.sensor_desc[eedata.sensor_index], sizeof(str));
            lcd_gotoxy(0, 0);
            printf_P(PSTR(MSG_MAIN_TEMP), str, temp.quot, temp.rem);
            
            temp = div(stdata.temperature[eedata.sensor_index2], 100);
            temp.rem = abs(temp.rem) / 10;
            ReadEEPromBlock(str, desc.sensor_desc[eedata.sensor_index2], sizeof(str));
            lcd_gotoxy(0, 1);
            printf_P(PSTR(MSG_MAIN_TEMP), str, temp.quot, temp.rem);
            
            lcd_gotoxy(0, 2);
            if (stdata.state.mode == MODE_EKV)
                printf_P(PSTR(MSG_MAIN_EKV), stdata.state.param, stdata.state.wall, stdata.state.dev);
            else if (stdata.state.mode == MODE_THSTAT)
                printf_P(PSTR(MSG_MAIN_THSTAT), stdata.state.param, stdata.state.dev);
            else
                printf_P(PSTR(MSG_MAIN_OFF));
            
            lcd_gotoxy(0, 3);
            printf_P(PSTR(MSG_MAIN_RTC), hour, min);
            
#endif
            break;
        }

        case ST_SHIFT:
        case ST_SHIFT_EDIT: {
            if (rwdata.shift_length)
                printf_P(PSTR(MSG_SHIFT_TIMED), rwdata.shift, rwdata.shift_length);
            else
                printf_P(PSTR(MSG_SHIFT_STATIC), rwdata.shift);
            
            break;
        }

        case ST_MODE:
        case ST_MODE_EDIT: {
            ReadEEPromBlock(str, desc.mode_desc[rwdata.mode], sizeof(str));
            printf_P(PSTR(MSG_MODE), str);
            break;
        }

        case ST_RTC:
        case ST_RTC_EDIT: {
            decode_date(rwdata.now, &year, &month, &day);
            decode_time(rwdata.now, &hour, &min, &sec);
            printf_P(PSTR(MSG_RTC), day, month, year, hour, min);
            break;
        }

        case ST_SWITCH:
        case ST_SWITCH_EDIT_M:
        case ST_SWITCH_EDIT_C: {
            if (rwdata.sw_mode < 0) {
                printf_P(PSTR(MSG_SWITCH_OFF));                
            } else {
                decode_date(rwdata.sw_datetime, &year, &month, &day);
                decode_time(rwdata.sw_datetime, &hour, &min, &sec);
                ReadEEPromBlock(str, desc.mode_desc[rwdata.sw_mode], sizeof(str));
                printf_P(PSTR(MSG_SWITCH_ON), str, day, month, year, hour, min);
            }
            break;
        }

        case ST_CONFIG:
        case ST_CONFIG_EDIT: {
            printf_P(PSTR(MSG_CONFIG), eedata.addr, eedata.contrast, eedata.reset ? MSG_YES : MSG_NO);
            break;
        }

        case ST_MESSAGE: {
            PGM_P txt = 0;
            
            switch (message) {
                case MSG_INIT: {
                    txt = PSTR(MSG_INIT_STR);
                    break;
                }
                case MSG_COMERR: {
                    txt = PSTR(MSG_COMERR_STR);
                    break;
                }
                case MSG_INITERR: {
                    txt = PSTR(MSG_INITERR_STR);
                    break;
                }
            }
            
            if (txt)
                printf_P(PSTR(MSG_MESSAGE_STR), txt);
            else
                printf_P(PSTR(MSG_MESSAGE_ID), message);
            
            if (message & 0x80)
                AvrXSendMessage(&BeepQueue, (message & 0x40) ? &BeepStart : &BeepOnce);

            break;
        }

        default:
            AvrXHalt();
    }
#if (DISPLAY == 1602)
    lcd_gotoxy(15, 0);
#elif (DISPLAY == 2004)
    lcd_gotoxy(19, 0);
#endif
    lcd_command(
        ((state == ST_SHIFT_EDIT) || (state == ST_MODE_EDIT) || (state == ST_RTC_EDIT) || (state == ST_SWITCH_EDIT_M) ||
         (state == ST_SWITCH_EDIT_C) || (state == ST_CONFIG_EDIT) || (state == ST_MESSAGE)
        ) ? LCD_DISP_ON_CURSOR_BLINK : LCD_DISP_ON
    );
}


/*************************************************************************
 * uloha MenuTask
 * min. stack 111B 
 *************************************************************************/ 
AVRX_GCC_TASKDEF(MenuTask, 140, 3)
{
    int i;
    signed char c;
    unsigned char state_old = ST_MAIN;
    unsigned char state_new;
    unsigned char j, key;
    unsigned char (*pStateFunc)(unsigned char key, unsigned char state);
    
    /* inicializace */
    if (ReadKey() != (KEY_PLUS | KEY_MINUS)) { // plus+minus pri staru = preskoc init
        message = MSG_INIT;
        MenuInternalRefresh(ST_MESSAGE);
        AvrXDelay(&MenuDelayTimer, PACKET_ACK_DELAY);
        for (i = 0; i < PACKET_TX_TRIES; i++) {
            Serial_RWDATA_R();
            AvrXDelay(&MenuDelayTimer, PACKET_RX_TIMEOUT);
            j = (AvrXTestSemaphore(&RWDATA_Written) == SEM_DONE);
            if (j)
                break;
            AvrXDelay(&MenuDelayTimer, PACKET_ACK_DELAY);
        }
        if (!j) {
            message = MSG_INITERR;
            MenuInternalRefresh(ST_MESSAGE);
            AvrXTaskExit();
        }
        for (i = 0; i < PACKET_TX_TRIES; i++) {
            Serial_STDATA_R();
            AvrXDelay(&MenuDelayTimer, PACKET_RX_TIMEOUT);
            j = (AvrXTestSemaphore(&STDATA_Written) == SEM_DONE);
            if (j)
                break;
            AvrXDelay(&MenuDelayTimer, PACKET_ACK_DELAY);
        }
        if (!j) {
            MenuMessage(MSG_INITERR);
        }
    }
    
    /* hlavni smycka */
    while (1) {
        state_new = state_old;
        c = AvrXWaitPullFifo(MenuFifo);
        if (c < 0) {
            // specialni pozadavek
            if (c == MENU_REFRESH) {
                state_old = ST_ERROR;   // zneplatnit stary stav
            } else if (c == MENU_MESSAGE) {
                state_new = ST_MESSAGE;                
            } else {
                AvrXHalt();
            }
            
        } else {
            // stisknuta klavesa - obsluha stavovym automatem
            for (i = 0; (j = pgm_read_byte(&menu_nextstate[i].state)); i++) {
                key = pgm_read_byte(&menu_nextstate[i].key);
                if ((j == state_old) && (key & c)) {
                    pStateFunc = (PGM_VOID_P)pgm_read_word(&menu_nextstate[i].pStateFunc);
                    if (pStateFunc) {
                        state_new = pStateFunc(c, state_old);
                        state_old = ST_ERROR;   // zneplatnit stary stav
                    } else {
                        state_new = pgm_read_byte(&menu_nextstate[i].nextstate);
                    }
                    break;
                }
            }

        }
        if (state_new != state_old) {
            MenuInternalRefresh(state_new);
            state_old = state_new;
        }
    }
}


/*************************************************************************
 * inicializace tasku
 *************************************************************************/ 
void InitMenuTask(void)
{
//  memset(MenuTaskStk, 0xA0A0, sizeof(MenuTaskStk));

    // inicializace ulohy
    AVRX_INIT_FIFO(MenuFifo);
    
    //inicializace displeje
    lcd_init(LCD_DISP_ON);
    LcdInitHiLevel();

    AvrXPutFifo(MenuFifo, MENU_REFRESH);
}

